/**********************************************************************
 *
 * GEOS - Geometry Engine Open Source
 * http://geos.osgeo.org
 *
 * Copyright (C) 2006 Refractions Research Inc.
 * Copyright (C) 2001-2002 Vivid Solutions Inc.
 *
 * This is free software; you can redistribute and/or modify it under
 * the terms of the GNU Lesser General Public Licence as published
 * by the Free Software Foundation.
 * See the COPYING file for more information.
 *
 **********************************************************************
 *
 * Last port: index/quadtree/DoubleBits.java rev. 1.7 (JTS-1.16)
 *
 **********************************************************************/

#include <geos/index/quadtree/DoubleBits.h>
#include <geos/util/IllegalArgumentException.h>

#include <string>
#include <cstring>
#include <bitset>

#if __STDC_IEC_559__
#define ASSUME_IEEE_DOUBLE 1
#else
#define ASSUME_IEEE_DOUBLE 0
#endif

#if ! ASSUME_IEEE_DOUBLE
#include <cmath>
#endif

namespace geos {
namespace index { // geos.index
namespace quadtree { // geos.index.quadtree

using namespace std;

double
DoubleBits::powerOf2(int exp)
{
    if(exp > 1023 || exp < -1022) {
        throw util::IllegalArgumentException("Exponent out of bounds");
    }
#if ASSUME_IEEE_DOUBLE
    int64 expBias = exp + EXPONENT_BIAS;
    int64 bits = expBias << 52;
    double ret;
    memcpy(&ret, &bits, sizeof(int64));
    return ret;
#else
    return pow(2.0, exp);
#endif
}

int
DoubleBits::exponent(double d)
{
    DoubleBits db(d);
    return db.getExponent();
}

double
DoubleBits::truncateToPowerOfTwo(double d)
{
    DoubleBits db(d);
    db.zeroLowerBits(52);
    return db.getDouble();
}

string
DoubleBits::toBinaryString(double d)
{
    DoubleBits db(d);
    return db.toString();
}

double
DoubleBits::maximumCommonMantissa(double d1, double d2)
{
    if(d1 == 0.0 || d2 == 0.0) {
        return 0.0;
    }
    DoubleBits db1(d1);
    DoubleBits db2(d2);
    if(db1.getExponent() != db2.getExponent()) {
        return 0.0;
    }
    int maxCommon = db1.numCommonMantissaBits(db2);
    db1.zeroLowerBits(64 - (12 + maxCommon));
    return db1.getDouble();
}

/*public*/
DoubleBits::DoubleBits(double nx)
{
#if ASSUME_IEEE_DOUBLE
    memcpy(&xBits, &nx, sizeof(double));
#endif
    x = nx;
}

/*public*/
double
DoubleBits::getDouble() const
{
    return x;
}

/*public*/
int64
DoubleBits::biasedExponent() const
{
    int64 signExp = xBits >> 52;
    int64 exp = signExp & 0x07ff;
    //cerr<<"xBits:"<<xBits<<" signExp:"<<signExp<<" exp:"<<exp<<endl;
    return exp;
}

/*public*/
int
DoubleBits::getExponent() const
{
#if ASSUME_IEEE_DOUBLE
    return static_cast<int>(biasedExponent() - EXPONENT_BIAS);
#else
    if(x <= 0) {
        return 0;    // EDOM || ERANGE
    }
    return (int)((log(x) / log(2.0)) + (x < 1 ? -0.9 : 0.00000000001));
#endif
}

/*public*/
void
DoubleBits::zeroLowerBits(int nBits)
{
    long invMask = (1L << nBits) - 1L;
    long mask = ~invMask;
    xBits &= mask;
}

/*public*/
int
DoubleBits::getBit(int i) const
{
    long mask = (1L << i);
    return (xBits & mask) != 0 ? 1 : 0;
}

/*public*/
int
DoubleBits::numCommonMantissaBits(const DoubleBits& db) const
{
    for(int i = 0; i < 52; i++) {
        if(getBit(i) != db.getBit(i)) {
            return i;
        }
    }
    return 52;
}

/*public*/
string
DoubleBits::toString() const
{
    int64 bit;
    memcpy(&bit, &x, sizeof(x));
    string bitStr = bitset<64>(bit).to_string();

    string str = bitStr.substr(0, 1) + "  "
        + bitStr.substr(1, 11) + "(" + std::to_string(getExponent()) + ") "
        + bitStr.substr(12)
        + " [ " + std::to_string(x) + " ]";

    return str;
}

} // namespace geos.index.quadtree
} // namespace geos.index
} // namespace geos
